﻿#ifndef ___PCLIB_S_FSWATCHER___
#define ___PCLIB_S_FSWATCHER___


#include "../m_core/m_core.hpp"


/**
*@brief     文件系统发生改变的动作枚举
*/
enum PCFSAction
{
    /// 创建或重命名（被创建）
    FSEvtAdd = 1,
    /// 删除或重命名（被删除）
    FSEvtDelete = 2,
    /// 被改变
    FSEvtModified = 4
};

/**
*@brief     文件系统监控任务基类
*           处理文件监视任务的平台无关部分
*/
class CPCFSWatcherTaskBase : CPCNoCopyable
{
public:
    /**
    *@brief     文件系统发生改变的回调
    *@param     dir         [in]    监视的目录名
    *@param     filename    [in]    发生改变的文件名
    *@param     action      [in]    发生改变的具体动作
    */
    typedef std::function<void(const char* dir, const char* filename, PCFSAction action)> PCFSWatchCallBack;

public:
    std::string m_Dir;
    bool        m_IsRecursive;
    PCFSWatchCallBack m_CallBack;
};

#if defined(PCOS_WIN)

/**
*@brief     overlapped结构带一个指针
*           一般是用在重叠函数里面，指针用来放this
*/
struct PCOVERLAPPED_EX
{
    OVERLAPPED  overlapped;
    void*       ptr;
};

/**
*@brief     文件系统监控任务<windows>
*           单个目录的监视任务
*           在目录（文件）发生改变时用户可以通过回调得到通知
*/
class CPCFSWatcherTask : public CPCFSWatcherTaskBase
{
public:
    explicit CPCFSWatcherTask()
        :m_DirHandle(NULL)
        , m_StopNow(true)
    {
        m_Dir = "";
        m_IsRecursive = false;
    }
    ~CPCFSWatcherTask()
    {}

    /**
    *@brief     启动监视任务
    *@param     dir         [int]    要监视的目录
    *@param     recursive   [int]    是否监视子目录
    *@param     callback    [int]    回调函数
    *@return    是否成功
    */
    CPCResult<> Start(const char* dir, bool recursive, PCFSWatchCallBack callback)
    {
        CPCResult<> result;
        if (dir == NULL || dir[0] == 0)
        {
            return result.SetFail("params invalid");
        }
        if (m_DirHandle != NULL)
        {
            return result.SetFail("service is started already,please stop it first.");
        }
        m_Dir = dir;
        m_IsRecursive = recursive;
        m_CallBack = callback;
        m_DirHandle = CreateFileA(m_Dir.c_str(),
            FILE_LIST_DIRECTORY,
            FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
            NULL,
            OPEN_EXISTING,
            FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, NULL);
        if (INVALID_HANDLE_VALUE == m_DirHandle)
        {
            return result.SetSystemFail();
        }

        m_Overlapped.ptr = this;
        m_Overlapped.overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
        if (NULL == m_Overlapped.overlapped.hEvent)
        {
            PCCloseHandle(m_DirHandle);
            return result.SetSystemFail();
        }
        if (!RefreshWatch(false))
        {
            PCCloseHandle(m_Overlapped.overlapped.hEvent);
            PCCloseHandle(m_DirHandle);
            return result.SetSystemFail();
        }
        m_StopNow = false;
        return result;
    }

    /**
    *@brief     移除监视任务
    */
    void Stop()
    {
        m_StopNow = true;
        if (m_DirHandle != NULL && m_DirHandle != INVALID_HANDLE_VALUE)
        {
            CancelIo(m_DirHandle);
            RefreshWatch(true);
        }
        if (m_Overlapped.overlapped.hEvent != NULL && !HasOverlappedIoCompleted(&m_Overlapped.overlapped))
        {
            SleepEx(5, TRUE);
        }
        PCCloseHandle(m_Overlapped.overlapped.hEvent);
        PCCloseHandle(m_DirHandle);
    }

    /**
    *@brief     启动消息循环
    *           这个函数需要在所有文件监控对象都开始后在主线程里面开个循环一直调用
    */
    static void Update()
    {
        MsgWaitForMultipleObjectsEx(0, NULL, 0, QS_ALLINPUT, MWMO_ALERTABLE);
    }

protected:

    //读取文件改变
    bool RefreshWatch(bool clear)
    {
        DWORD NotifyFilter = FILE_NOTIFY_CHANGE_CREATION |
            FILE_NOTIFY_CHANGE_SIZE |
            FILE_NOTIFY_CHANGE_FILE_NAME |
            FILE_NOTIFY_CHANGE_LAST_WRITE;
        return ReadDirectoryChangesW(
            m_DirHandle, m_Buffer, sizeof(m_Buffer), m_IsRecursive,
            NotifyFilter, NULL, (LPOVERLAPPED)&m_Overlapped, clear ? 0 : WatchCallback) != 0;
    }

    // 调用回调函数处理动作
    void handleAction(const char* filename, unsigned long action)
    {
        PCFSAction fwAction;
        switch (action)
        {
        case FILE_ACTION_RENAMED_NEW_NAME:
        case FILE_ACTION_ADDED:
            fwAction = FSEvtAdd;
            break;
        case FILE_ACTION_RENAMED_OLD_NAME:
        case FILE_ACTION_REMOVED:
            fwAction = FSEvtDelete;
            break;
        case FILE_ACTION_MODIFIED:
            fwAction = FSEvtModified;
            break;
        default:
            return;
        };

        if (m_CallBack)
        {
            m_CallBack(m_Dir.c_str(), filename, fwAction);
        }
    }

    /// 系统回调函数
    static void CALLBACK WatchCallback(DWORD dwErrorCode, DWORD dwNumberOfBytesTransfered, LPOVERLAPPED lpOverlapped)
    {
        char szFile[MAX_PATH];
        PFILE_NOTIFY_INFORMATION pNotify;
        PCOVERLAPPED_EX* olEx = (PCOVERLAPPED_EX*)lpOverlapped;
        CPCFSWatcherTask* pWatch = (CPCFSWatcherTask*)olEx->ptr;
        size_t offset = 0;

        if (dwNumberOfBytesTransfered == 0)
            return;

        if (dwErrorCode == ERROR_SUCCESS)
        {
            do
            {
                pNotify = (PFILE_NOTIFY_INFORMATION)&pWatch->m_Buffer[offset];
                offset += pNotify->NextEntryOffset;
                int count = WideCharToMultiByte(CP_ACP, 0, pNotify->FileName,
                    pNotify->FileNameLength / sizeof(WCHAR),
                    szFile, MAX_PATH - 1, NULL, NULL);
                szFile[count] = TEXT('\0');
                pWatch->handleAction(szFile, pNotify->Action);

            } while (pNotify->NextEntryOffset != 0);
        }

        if (!pWatch->m_StopNow)
        {
            pWatch->RefreshWatch(false);
        }
    }

protected:
    HANDLE      m_DirHandle;
    PCOVERLAPPED_EX m_Overlapped;
    unsigned char   m_Buffer[32 * 1024];
    bool        m_StopNow;
};

#elif defined(PCOS_LINUX)

/**
*@brief     文件系统监控任务<linux/android>
*           单个目录的监视任务
*/
class CPCFSWatcherTask : public CPCFSWatcherTaskBase
{
public:
    explicit CPCFSWatcherTask()
    {
    }
    ~CPCFSWatcherTask()
    {}

    /**
    *@brief     启动监视任务
    *@param     dir         [int]    要监视的目录
    *@param     recursive   [int]    是否监视子目录
    *@param     callback    [int]    回调函数
    *@return    是否成功，成功时IntResult()可获取watchID，可以用这个watchID对任务进行其他操作
    */
    CPCResult<> Start(const char* dir, bool recursive, PCFSWatchCallBack callback)
    {
        CPCResult<> result;
        return result;
    }

    void Stop()
    {

    }

    static void Update()
    {

    }

protected:
};

#else

/**
*@brief     文件系统监控任务<macos/ios>
*           单个目录的监视任务
*/
class CPCFSWatcherTask : public CPCFSWatcherTaskBase
{
public:
    explicit CPCFSWatcherTask(int watchId, const char* dir, bool recursive, PCFSWatchCallBack callback)
    {
    }
    ~CPCFSWatcherTask()
    {}

    /**
    *@brief     启动监视任务
    *@param     dir         [int]    要监视的目录
    *@param     recursive   [int]    是否监视子目录
    *@param     callback    [int]    回调函数
    *@return    是否成功，成功时IntResult()可获取watchID，可以用这个watchID对任务进行其他操作
    */
    CPCResult<> Start(const char* dir, bool recursive, PCFSWatchCallBack callback)
    {
        CPCResult<> result;
        return result;
    }

    void Stop()
    {

    }

    static void Update()
    {

    }

protected:
};

#endif



#endif    //___PCLIB_S_FSWATCHER___